﻿#define TRACE
using System;
using System.IO;
using System.IO.Ports;
using System.Threading;
using System.Diagnostics;
using UdpTransceiver;
using ParametersParser;
using System.Xml.Linq;
using System.Collections;

using Communicator;
using ArduinoStepper;

/**********************************************
* The module for the control of indicators    *
* of the TL410 air simulator of NAU           *
* Developed by Vladimir Khomenko, 2010        *
**********************************************/

namespace Indicators
{
    using System.Timers;
    /// <summary>
    /// Перечисление результатов операции
    /// </summary>
    public enum ErrorCodes { OK, Error };
    /// <summary>
    /// Тип индикатора - круговой, угловой, многооборотный 
    /// </summary>
    public enum IndicatorTypes { Dial, Limited, Multirotor };
    /// <summary>
    /// Тип инициализации индикатора.
    /// </summary>
    public enum IndicatorInitType { INTERNAL_SENSOR, BY_NETWORK, WITHOUT_SENSOR }
    /// <summary>
    /// Состояния индикатора.
    /// </summary>
    public enum IndicatorStates { Started, PositionRestoring, SensorSearching, ZeroResetting,  
                                    ReceiverInitialization, DeltaChecking, DeltaReducingCommand, ParameterWaiting, Tracking, 
                                    ZeroCorrection, Fault, Stoped }

    /// <summary>
    /// События индикатора.
    /// </summary>
    public enum IndicatorEvents { Initiated, SensorSearchCommandSent, ControllerStateReceived, SensorFound, SensorNotFound, DataReceived, RecordedPositionRestored, ZeroReseted,
                                    ParameterUpdated, SensorSet, CriticalError, ResetCommand }

    /// <summary>
    /// Представляет обобщенный индикатор 
    /// </summary>
    public abstract class Indicator
    {
        #region Class data members
        /// <summary>
        /// Название индикатора. Читается из конфигуратора
        /// </summary>
        public string Name;
        /// <summary>
        /// Контроллер, к которым управляется индикатор
        /// </summary>
        public Controller Controller;
        public Communicator.Communicator Communicator;
        /// <summary>
        /// Номер мотора, выделенный индикатору и управляемый контроллером
        /// </summary>
        public byte MotorNumber;
        /// <summary>
        /// Тип индикатора
        /// </summary>
        public IndicatorTypes IndicatorType;
        /// <summary>
        /// Значение параметра модели, соответствующее нулевому положению индикатора. (читается из конфигуратора)
        /// </summary>
        public float ModelZeroValue = 0f;
        /// <summary>
        /// Коэффициент редукции угла (отношение угла поворота двигателя к углу поворота индикатора) (читается из конфигуратора)
        /// </summary>
        public float AngleFactor = 1f;
        /// <summary>
        /// Угловое положение мотора, которое считается начальным после запуска. 
        /// </summary>
        public float InitialMotorAngle = 0;
        /// <summary>
        /// Смещение ротора относительно датчика перед точным его поиском (читается из конфигуратора)
        /// </summary>
        public float InitialSearchShift = 0f;
        /// <summary>
        /// Максимальный угол поворота индикатора при поиске датчика (читается из конфигуратора)
        /// </summary>
        public float MaxSearchAngle = 360f;
        /// <summary>
        /// Скорость вращения при поиске датчика (градусов в секунду) - пока не используется (читается из конфигуратора)
        /// </summary>
        public float SearchSpeed = 0f;
        /// <summary>
        /// Тип инициализации индикатора (Network, internal sensor or without sensor)
        /// </summary>
        public IndicatorInitType InitType;
        /// <summary>
        /// Угловое положение датчика относительно нуля индикатора (читается из конфигуратора)
        /// </summary>
        public float SensorAngle = 0f;
        /// <summary>
        /// Состояние датчика (??????)
        /// </summary>
        public bool SensorState = false;
        /// <summary>
        /// Наименование параметра модели, управляющего индикатором (читается из конфигуратора)
        /// </summary>
        public string ParName;
        /// <summary>
        /// Текущее значение индикатора.
        /// </summary>
        public float TargetIndicatorValue = 0f;
        /// <summary>
        /// Текущее значение индикатора.
        /// </summary>
        public float LastStopedIndicatorValue = 0f;
        /// <summary>
        /// Текущее значение параметра модели.
        /// </summary>
        public float ModelValue = 0f;
        /// <summary>
        /// Состояние индикатора, начальное - запущен.
        /// </summary>
        public IndicatorStates State = IndicatorStates.Started;
        public IndicatorEvents IndEvent;
        /// <summary>
        /// Вычислительный поток индикатора.
        /// </summary>
        public Thread IndicatorThread;

        /// <summary>
        /// Настройка трассировки работы
        /// </summary>
        internal static SourceSwitch traceSwitch = new SourceSwitch("Indicators") { Level = SourceLevels.Information };
        internal static TraceSource trace = new TraceSource("Indicators") { Switch = traceSwitch };
        
        protected Timer IndicatorTimer;
        protected UInt64 TimeCounter = 0;

        
        protected string PrevResetParameterValue = "";
        protected string TotalResetParameterName = "TotReset";

        #endregion
        /// <summary>
        /// Метод создания конечного автомата
        /// </summary>
        public abstract void CreateStateMachine();
        /// <summary>
        /// Метод отслеживания положения индикатора
        /// </summary>
        public abstract void Track();
        /// <summary>
        /// Метод, инициирующий движение индикатора к заданному положению
        /// </summary>
        /// <param name="Value">Желаемое положение индикатора</param>
        /// <returns></returns>
        public abstract void MoveIndicator(float Value);
 
        /// <summary>
        /// Установка индикатора в заданное положение
        /// </summary>
        /// <param name="Value">Заданное положение индикатора</param>
        /// <returns>Success of error code</returns>
        public abstract void SetIndicator(float Value);
        //public abstract ErrorCodes ShiftIndicator(float Value);

         public abstract void UpdateParameter(object sender, EventArgs e);
        
        /// <summary>
        /// Обработка события приема данных коммуникатором  
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void OnDataReceived(object sender, EventArgs e)
        {
            if (PrevResetParameterValue == "0" && Communicator.GetParameterValueByName(this.TotalResetParameterName) != "1")
                IndEvent = IndicatorEvents.ResetCommand;
            else
            {
                PrevResetParameterValue = Communicator.GetParameterValueByName(this.TotalResetParameterName);
                Communicator.DataReceived -= new EventHandler<EventArgs>(OnDataReceived);
                IndEvent = IndicatorEvents.DataReceived;
                this.ProgressEvent.Set();
            }
        }

        /// <summary>
        /// Ручное событие для организации цикла ожидания автомата состояния.
        /// </summary>
        protected AutoResetEvent ProgressEvent;
        protected AutoResetEvent ControllerStateReceivedEvent;

        /// <summary>
        /// Конструктор индикатора
        /// </summary>
        /// <param name="name">Название индикатора</param>
        /// <param name="controller">Контроллер</param>
        /// <param name="motorNumber">Номер мотора</param>
        /// <param name="communicator">Коммуникатор</param>
        public Indicator(string name, Controller controller, byte motorNumber, Communicator.Communicator communicator)
        {
            this.Name = name;
            this.Controller = controller;
            this.MotorNumber = motorNumber;
            this.Communicator = communicator;
            ProgressEvent = new AutoResetEvent(false);
            ControllerStateReceivedEvent = new AutoResetEvent(false);
            this.IndicatorTimer = new Timer(100);
            this.IndicatorTimer.AutoReset = true;
            IndicatorTimer.Elapsed += delegate
            {
                ProgressEvent.Set();
            };

            this.CreateStateMachine();
            this.IndicatorThread = new Thread(LoopIndicator);
            this.IndicatorThread.Name = Name + " indicator";
            //IndicatorTimer.Start();
            // Default values
            trace.TraceInformation("Indicator {0} {1} indicator was created on controller on port {2} on motor# {3}", 
                                   this.Name, IndicatorType,  Controller.TR.PortName, MotorNumber);
        }

        protected void LoopIndicator()
        {
            while (this.State != IndicatorStates.Stoped)
            {
                HandleStateChangeEvent(this.IndEvent);
                // Подвешиваем цикл до срабатывания таймера
                this.ProgressEvent.WaitOne();
            }
        }


        /// <summary>
        /// Запускает индикатор
        /// </summary>
        public void Start()
        {
            // Запускаем коммуникатор
            if (Communicator.state != CommunicatorState.active)
                Communicator.Start();
            // Запускаем контроллер
            if (Controller.Active != true)
                Controller.Start();
            IndEvent = IndicatorEvents.Initiated;
            ProgressEvent.Set();
            TimeCounter = 0;

            // Подписываемся на события
            Controller.StateReceived += new EventHandler<EventArgs>(OnStateReceived);
            // Controller.FreeSensorChanged += new EventHandler<SensorsEventArgs>(OnUpdateSensor);
            Controller.SearchEnded += new EventHandler<SensorsEventArgs>(OnSearchEnded);
            // Создаем и запускаем автомат состояний
            //IndicatorThread = new Thread(this.CreateStateMachine);
            //IndicatorThread.Name = this.Name;
            IndicatorThread.Start();
        }

        /// <summary>
        /// Останавливает индикатор
        /// </summary>
        public void Stop()
        {
            Communicator.Stop();
            Controller.Stop();
            IndicatorThread.Abort();
        }

        /// <summary>
        /// Ищет датчик начального положения индикатора. 
        /// </summary>
        /// <returns>Code of success or error</returns>
        public void FindSensor()
        {
            // Вычисляем угол ротора мотора, где может находится датчик
            float targetAngle = TargetIndicatorValue - SensorAngle;
            // Пробуем повернуть индикатор в положение поближе к датчику
            SetIndicator(targetAngle);
             // Даем команду на поиска датчика. 
            Controller.Motors[this.MotorNumber].FindFreeSensor(MaxSearchAngle);
         }

        /// <summary>
        /// Обработчик события 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void OnSearchEnded(object sender, SensorsEventArgs e)
        {
            if (e.Motor == this.MotorNumber)
            {
                if (e.State == true)
                    IndEvent = IndicatorEvents.SensorFound;
                else
                    IndEvent = IndicatorEvents.SensorNotFound;
                this.ProgressEvent.Set();
            }
        }

        /// <summary>
        /// Обработчик события изменения состояния датчика
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void OnUpdateSensor(object sender, SensorsEventArgs e)
        {
            // Запоминаем предыдущее состояние датчика
            Boolean prevSensorState = this.SensorState;
            // Отмечаем новое состояние датчика
            if (e.Motor == this.MotorNumber)
                this.SensorState = e.State;
            // Если сенсор перешел из выключенного состояния во включенное, генерируем событие "датчик включился"
            if (prevSensorState == false && this.SensorState == true)
            {
                IndEvent = IndicatorEvents.SensorSet;
                this.ProgressEvent.Set();
            }
        }

        // Обработчик события приема слова состояния из МК.
        public void OnStateReceived(object sender, EventArgs e)
        {
            this.LastStopedIndicatorValue = Controller.Motors[this.MotorNumber].AnglePosition / AngleFactor ;
            // Устанавливаем ручное событие приема слова из контроллера
            ControllerStateReceivedEvent.Set();
        }

        /// <summary>
        /// Читает последнюю записанную позицию индикатора из журнала
        /// </summary>
        protected void RestoreRecordedPosition()
        {
            // Открываем файл с последним значнием индикатора во время предыдущей работы - вероятно индикатор остался в том же положении
            XDocument IndicatorValuesLog = XDocument.Load(Name + ".xml");
            // Записываем восстановленное положение индикатора как текущее
            this.TargetIndicatorValue = Convert.ToSingle(IndicatorValuesLog.Element("Values").Element(this.Name).Value);
            // Запрашиваем МК о значении счетчика двигателя
            Controller.SendControllerStateRequest(this.MotorNumber);
            // Подвешиваем процедуру до получени ответа.
            this.ControllerStateReceivedEvent.WaitOne();
            // Считываем угол ротора и запоминаем его как начальный
            this.InitialMotorAngle = Controller.Motors[this.MotorNumber].AnglePosition;
            
            trace.TraceInformation("Indicator " + Name + " restore the last position of previous work =" + this.TargetIndicatorValue.ToString());
            // Генерируем событие окончания восстановления значений.
            IndEvent =  IndicatorEvents.RecordedPositionRestored;
            this.ProgressEvent.Set();
        }

        /// <summary>
        /// Инициализирует прием параметров из сети
        /// </summary>
        protected void InitReceiver()
        {
            Communicator.AddParameter(this.TotalResetParameterName, ParameterDirection.input);
            Communicator.AddParameter(this.ParName, ParameterDirection.input);
            Communicator.DataReceived += new EventHandler<EventArgs>(UpdateParameter);
            Communicator.DataReceived += new EventHandler<EventArgs>(OnDataReceived);
        }

        /// <summary>
        /// Переключает индикатор в состояние сбоя
        /// </summary>
        protected void SetFault()
        {
            Communicator.AddParameter(this.TotalResetParameterName, ParameterDirection.input);
            Communicator.DataReceived += new EventHandler<EventArgs>(OnDataReceived);
            //Track();
        }

        protected void ResetZero()
        {
            Thread.Sleep(200);            
            // Перемещаем ротор от датчика к нулевой позиции
            // Сброс счетчика двигателя в контроллере
            Controller.Motors[this.MotorNumber].Reset();
            Thread.Sleep(200);
            this.InitialMotorAngle = 0;
            this.TargetIndicatorValue = 0;
            SetIndicator(-SensorAngle);
            Thread.Sleep(200);
            // Сброс счетчика двигателя в контроллере
            Controller.Motors[this.MotorNumber].Reset();
            Thread.Sleep(200);
            this.InitialMotorAngle = 0;
            this.TargetIndicatorValue = 0;          
 
            // Пишем положение индикатора в файл
            XDocument IndicatorsValuesLog = XDocument.Load(Name + ".xml");
            IndicatorsValuesLog.Element("Values").Element(this.Name).SetValue(TargetIndicatorValue.ToString());
            IndicatorsValuesLog.Save(Name + ".xml");
            IndEvent = IndicatorEvents.ZeroReseted;
            this.ProgressEvent.Set();
        }

        protected void Nothing()
        {
            ;
        }

        /// <summary>
        /// Initialize indicator with the absent zero sensor. Can be used at the testing mode.
        /// </summary>
        /// <returns>Code of success or error</returns>
        public ErrorCodes init_without_sensor()
        {
            TargetIndicatorValue = ModelZeroValue;
            return ErrorCodes.OK;
        }

        /// <summary>
        /// Событие окончания поиска датчика положения индикатора
        /// </summary>
        public event EventHandler<SensorFindArgs> SensorSearchCompleted;
        /// <summary>
        /// Обработчик события окончания поиска датчика индикатора
        /// </summary>
        /// <param name="e"></param>
        public void OnSensorSearchCompleted(SensorFindArgs e)
        {
            if (SensorSearchCompleted != null)
                SensorSearchCompleted(this, e);
        }


        /// <summary>
        /// Обработчик событий, перебирающий таблицу состояний-переходов и вызывающий необходимые действия
        /// в зависимости от исходного состояния и события
        /// </summary>
        /// <param name="e">Событие, порождающее переход</param>
        protected void HandleStateChangeEvent(IndicatorEvents e)
        {

             // Проходим по списку переходв
            foreach (Transition transition in transitions)
            {
                //Ищем соответствующее начальное состояние и событие
                if (State == transition.startState && e == transition.trigger)
                {

                    //Console.WriteLine("Old state = {0}\t, new state = {1}", transition.startState, transition.endState);
                    // Console.WriteLine("IndicatorAngle = {0},\t ModelAngle = {1}", this.IndicatorValue, this.ModelValue);
                    trace.TraceEvent(TraceEventType.Information, 9, "Indicator {0} has changed state {1} to state {2} by event {3}", this.Name, transition.startState, transition.endState, Enum.GetName(typeof(IndicatorEvents), e));
                    //Устанавливаем следующее состояние
                    State = transition.endState;
                    //Вызываем действия, которые необходимо выполнить при переходе
                    transition.action();
                    break;
                }
            }

        }

        /// Список переходов состояний коммутатора
        /// </summary>
        private IList transitions = new ArrayList();
        /// <summary>
        /// Делегат действий, выполняемых при переходе состояний
        /// </summary>
        protected delegate void IndicatorAction();
        //public event EventHandler<IndicatorEventArgs> IndicatorEvent;

        /// <summary>
        /// Добавление строки переходов в список переходов коммутатора
        /// </summary>
        /// <param name="start">Текущее состояние</param>
        /// <param name="e">Событие</param>
        /// <param name="end">Новое состояние</param>
        /// <param name="action">Действие, выполняемое при переходе</param>
        protected void AddTransition(IndicatorStates start, IndicatorEvents e, IndicatorStates end, IndicatorAction action)
        {
            // Создаем объект - переход и заполняем его
            transitions.Add(new Transition(start, e, end, action));
        }
        /// <summary>
        /// Представляет один переход 
        /// </summary>
        private class Transition
        {
            public IndicatorStates startState;
            public IndicatorEvents trigger;
            public IndicatorStates endState;
            public IndicatorAction action;

            public Transition(IndicatorStates start, IndicatorEvents e, IndicatorStates end, IndicatorAction a)
            {
                this.startState = start;
                this.trigger = e;
                this.endState = end;
                this.action = a;
            }
        }

    }

    // Параметры события окончания поиска датчика
    public class SensorFindArgs : EventArgs
    {
        public Boolean SensorIsFound;
        public SensorFindArgs(Boolean sensorIsFound)
        {
            this.SensorIsFound = sensorIsFound;
        }
    }

    /// <summary>
    /// Реализация специального класса кругового стрелочного индикатора, который поворачивается на 0-360 градусов без ограничителя.
    /// </summary>
    public class DialIndicator: Indicator
    {
        public DialIndicator(string name, Controller Controller, byte MotorNumber, Communicator.Communicator communicator) 
            : base(name, Controller, MotorNumber, communicator) 
        {
            this.IndicatorType = IndicatorTypes.Dial;
        }

        /// <summary>
        /// Преобразует значение угла к диапазону 0..360 
        /// </summary>
        /// <param name="value">Угол</param>
        /// <returns>Угол в диапазоне 0..360</returns>
        private float convertToDial(float value)
        {
            value = value - 360 * (short)(value / 360); // Normalization to ( value < abs(360) )
            if (value < 0)
                value = 360 + value; // Normalization to ( value = 0..360 )
            return value;
        }
        
        /// <summary>
        /// Сложение нормализованных углов
        /// </summary>
        /// <param name="a">Первый угол</param>
        /// <param name="b">Второй угол</param>
        /// <returns>Сумма углов, нормализованная к 0..360</returns>
        private float add(float a, float b)
        {
            return convertToDial( a + b );
        }
        
        /// <summary>
        /// Находит разницу мжеду углами с учетом кратчайшего расстояния (менее 180)  
        /// </summary>
        /// <param name="a">Current angle</param>
        /// <param name="b">Destination angle</param>
        /// <returns>Delta for moving</returns>
        private float differ (float a, float b)
        {
            float delta = convertToDial( a ) - convertToDial( b );
            if (delta > 180)
                delta = delta - 360;
            else if (delta < -180)
                delta = 360 + delta;

            return delta;
        }

        /// <summary>
        /// Устанавливает индикатор в заданную позицию. Ожидает, пока позиция не будет достигнута
        /// </summary>
        /// <param name="targetIndAngle">Заданная угловая позиция индикатора</param>
        /// <returns>Success or error code</returns>
        public override void SetIndicator(float targetIndAngle)
        {
            // Определяем требуемый угол ротора
            float motorAngle = this.InitialMotorAngle + targetIndAngle * AngleFactor;

            // Даем команду на поворот двигателя на заданный угол
            Controller.Motors[MotorNumber].SetAbsolute( motorAngle );
            // Запоминаем угол индикатора
            this.TargetIndicatorValue = targetIndAngle;
            // Трассируем
            trace.TraceInformation("Indicator " + Name +
                                                  " was set to " + targetIndAngle.ToString() +
                                                  "\tmotor was set to" + motorAngle.ToString());
            // Пишем значение индикатора в файл
            XDocument IndicatorsValuesLog = XDocument.Load(Name + ".xml");
            IndicatorsValuesLog.Element("Values").Element(this.Name).SetValue(convertToDial(this.TargetIndicatorValue).ToString());
            IndicatorsValuesLog.Save(Name +".xml");
        }

 
        /// <summary>
        /// Дает команду на перемещение индикатора в заданную позицию. Не ожидает достижения заданной позиции
        /// </summary>
        /// <param name="Value">Необходимая позиция индикатора</param>
        /// <returns>Success or error code</returns>
        public override void MoveIndicator(float targetIndAngle)
        {
            
            // Определяем требуемый угол ротора
            float motorAngle = InitialMotorAngle + targetIndAngle * AngleFactor;

            // Даем команду на поворот двигателя на заданный угол
            Controller.Motors[MotorNumber].MoveAbsolute( motorAngle );
            // Запоминаем угол индикатора
            this.TargetIndicatorValue = targetIndAngle;
            // Трассируем
            trace.TraceInformation("Indicator " + Name +
                                                  " was set to " + targetIndAngle.ToString() +
                                                  "\tmotor was set to" + motorAngle.ToString());
            // Пишем значение индикатора в файл
            XDocument IndicatorsValuesLog = XDocument.Load(Name + ".xml");
            IndicatorsValuesLog.Element("Values").Element(this.Name).SetValue(convertToDial(this.TargetIndicatorValue).ToString());
            IndicatorsValuesLog.Save(Name +".xml");
         }

        /// <summary>
        /// Обработка события изменения параметра модели, связанного с индикатором.  
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public override void UpdateParameter(object sender, EventArgs e)
        {
            // Запоминаем предыдущее значение параметра
            float prevValue = this.ModelValue;
            // Берем значение параметра из коммуникатора и обновляем значение модели
            this.ModelValue = Convert.ToSingle(Communicator.GetParameterValueByName(this.ParName));
            // Если разница между старым и новым значением более 1, то генерируем событие "Параметр изменился"
            if (Math.Abs(differ(ModelValue, prevValue)) >= 2)
            {
                IndEvent = IndicatorEvents.ParameterUpdated;
                this.ProgressEvent.Set();
            }
        }

        /// <summary>
        /// Отслеживает текущее положение индикатора
        /// </summary>
        public override void Track()
        {
            // Запрашиваем МК о значении счетчика двигателя
            Controller.SendControllerStateRequest(this.MotorNumber);
            // Подвешиваем процедуру до получени ответа.
            this.ControllerStateReceivedEvent.WaitOne();
            float IndPosition = Controller.Motors[this.MotorNumber].AnglePosition / AngleFactor;
            float dif = differ( ModelValue, IndPosition );
            float targetIndAngle = IndPosition + dif ;
            MoveIndicator(targetIndAngle);
        }
         
       public override void CreateStateMachine()
       {
           //Определение делегатов-действий, выполняемых при переходах состояний
           IndicatorAction restoreRecordedPosition = new IndicatorAction(RestoreRecordedPosition);
           IndicatorAction findSensor = new IndicatorAction(FindSensor);
           IndicatorAction resetZero = new IndicatorAction(ResetZero);
           IndicatorAction initReceiver = new IndicatorAction(InitReceiver);
           IndicatorAction track = new IndicatorAction(Track);
           IndicatorAction setFault = new IndicatorAction(SetFault);
           IndicatorAction nothing = new IndicatorAction(Nothing);
            
           // Заполнение списка переходов состояний по шаблону:
           // начальное состояние / событие / конечное состояние / выполняемые действия
           // Часть таблицы состояний для инициализации и поиска датчика
           AddTransition(IndicatorStates.Started, IndicatorEvents.Initiated, IndicatorStates.PositionRestoring, restoreRecordedPosition);
           AddTransition(IndicatorStates.PositionRestoring, IndicatorEvents.RecordedPositionRestored, IndicatorStates.SensorSearching, findSensor);
           AddTransition(IndicatorStates.SensorSearching, IndicatorEvents.SensorFound, IndicatorStates.ZeroResetting, resetZero);
           AddTransition(IndicatorStates.SensorSearching, IndicatorEvents.SensorNotFound, IndicatorStates.Fault, setFault);
           // Часть таблицы состояний для слежения
           AddTransition(IndicatorStates.ZeroResetting, IndicatorEvents.ZeroReseted, IndicatorStates.ReceiverInitialization, initReceiver);
           AddTransition(IndicatorStates.ReceiverInitialization, IndicatorEvents.DataReceived, IndicatorStates.Tracking, track);
           AddTransition(IndicatorStates.Tracking, IndicatorEvents.ParameterUpdated, IndicatorStates.Tracking, track);
           // Часть таблицы состояний для коррекции нуля 
           //AddTransition(IndicatorStates.Tracking, IndicatorEvents.SensorSet, IndicatorStates.ZeroCorrection, correctZero);
           //AddTransition(IndicatorStates.ZeroCorrection, IndicatorEvents.PositionCorrected, IndicatorStates.Tracking, track);           
           // Часть таблицы состояний для ошибок
           AddTransition(IndicatorStates.Tracking, IndicatorEvents.CriticalError, IndicatorStates.Fault, setFault);
           AddTransition(IndicatorStates.Fault, IndicatorEvents.ResetCommand, IndicatorStates.SensorSearching, findSensor);
           AddTransition(IndicatorStates.Fault, IndicatorEvents.SensorSet, IndicatorStates.SensorSearching, findSensor);
        }
    }


    /// <summary>
    /// Представляет шкальный стрелочный многооборотный индикатор (0-360 * n градусов, где n = 1,2,3..)
    /// </summary>
    public class MultiRotorIndicator : Indicator
    {
        public MultiRotorIndicator(string name, Controller Controller, byte MotorNumber, Communicator.Communicator communicator)
            : base(name, Controller, MotorNumber, communicator)
        {
            this.IndicatorType = IndicatorTypes.Multirotor;
        }

        /// <summary>
        /// Устанавливает индикатор в заданную позицию. Ожидает, пока позиция не будет достигнута
        /// </summary>
        /// <param name="targetIndAngle">Заданная угловая позиция индикатора</param>
        /// <returns>Success or error code</returns>
        public override void SetIndicator(float targetIndAngle)
        {
            // Определяем требуемый угол ротора
            float motorAngle = this.InitialMotorAngle + targetIndAngle * AngleFactor;

            // Даем команду на поворот двигателя на заданный угол
            Controller.Motors[MotorNumber].SetAbsolute(motorAngle);
            // Запоминаем угол индикатора
            this.TargetIndicatorValue = targetIndAngle;
            // Трассируем
            trace.TraceInformation("Indicator " + Name +
                                                  " was set to " + targetIndAngle.ToString() +
                                                  "\tmotor was set to" + motorAngle.ToString());
            // Пишем значение индикатора в файл
            XDocument IndicatorsValuesLog = XDocument.Load(Name + ".xml");
            IndicatorsValuesLog.Element("Values").Element(this.Name).SetValue(this.TargetIndicatorValue.ToString());
            IndicatorsValuesLog.Save(Name + ".xml");
        }


        /// <summary>
        /// Дает команду на перемещение индикатора в заданную позицию. Не ожидает достижения заданной позиции
        /// </summary>
        /// <param name="Value">Необходимая позиция индикатора</param>
        /// <returns>Success or error code</returns>
        public override void MoveIndicator(float targetIndAngle)
        {
            // Определяем требуемый угол ротора
            float motorAngle = this.InitialMotorAngle + targetIndAngle * AngleFactor;

            // Даем команду на поворот двигателя на заданный угол
            Controller.Motors[MotorNumber].MoveAbsolute(motorAngle);
            // Запоминаем угол индикатора
            this.TargetIndicatorValue = targetIndAngle;
            // Трассируем
            trace.TraceInformation("Indicator " + Name +
                                                  " was set to " + targetIndAngle.ToString() +
                                                  "\tmotor was set to" + motorAngle.ToString());
            // Пишем значение индикатора в файл
            XDocument IndicatorsValuesLog = XDocument.Load(Name + ".xml");
            IndicatorsValuesLog.Element("Values").Element(this.Name).SetValue(this.TargetIndicatorValue.ToString());
            IndicatorsValuesLog.Save(Name + ".xml");
        }


        /// <summary>
        /// Обработка события изменения параметра модели, связанного с индикатором.  
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public override void UpdateParameter(object sender, EventArgs e)
        {
            // Запоминаем предыдущее значение параметра
            float prevValue = this.ModelValue;
            // Берем значение параметра из коммуникатора и обновляем значение модели
            this.ModelValue = Convert.ToSingle(Communicator.GetParameterValueByName(this.ParName));
            // Если разница между старым и новым значением более 1, то генерируем событие "Параметр изменился"
            if (Math.Abs(ModelValue - prevValue) >= 2)
                HandleStateChangeEvent(IndicatorEvents.ParameterUpdated);
        }

        /// <summary>
        /// Отслеживает текущее положение индикатора
        /// </summary>
        public override void Track()
        {
            MoveIndicator(this.ModelValue);
        }

        public override void CreateStateMachine()
        {
            //Определение делегатов-действий, выполняемых при переходах состояний
            IndicatorAction restoreRecordedPosition = new IndicatorAction(RestoreRecordedPosition);
            IndicatorAction findSensor = new IndicatorAction(FindSensor);
            IndicatorAction resetZero = new IndicatorAction(ResetZero);
            IndicatorAction initReceiver = new IndicatorAction(InitReceiver);
            IndicatorAction track = new IndicatorAction(Track);
            IndicatorAction setFault = new IndicatorAction(SetFault);
            IndicatorAction nothing = new IndicatorAction(Nothing);

            // Заполнение списка переходов состояний по шаблону:
            // начальное состояние / событие / конечное состояние / выполняемые действия
            // Часть таблицы состояний для инициализации и поиска датчика
            AddTransition(IndicatorStates.Started, IndicatorEvents.Initiated, IndicatorStates.PositionRestoring, restoreRecordedPosition);
            AddTransition(IndicatorStates.PositionRestoring, IndicatorEvents.RecordedPositionRestored, IndicatorStates.SensorSearching, findSensor);
            AddTransition(IndicatorStates.SensorSearching, IndicatorEvents.SensorFound, IndicatorStates.ZeroResetting, resetZero);
            AddTransition(IndicatorStates.SensorSearching, IndicatorEvents.SensorNotFound, IndicatorStates.Fault, setFault);
            // Часть таблицы состояний для слежения
            AddTransition(IndicatorStates.ZeroResetting, IndicatorEvents.ZeroReseted, IndicatorStates.ReceiverInitialization, initReceiver);
            AddTransition(IndicatorStates.ReceiverInitialization, IndicatorEvents.DataReceived, IndicatorStates.Tracking, track);
            AddTransition(IndicatorStates.Tracking, IndicatorEvents.ParameterUpdated, IndicatorStates.Tracking, track);
            // Часть таблицы состояний для коррекции нуля 
            //AddTransition(IndicatorStates.Tracking, IndicatorEvents.SensorSet, IndicatorStates.ZeroCorrection, correctZero);
            //AddTransition(IndicatorStates.ZeroCorrection, IndicatorEvents.PositionCorrected, IndicatorStates.Tracking, track);           
            // Часть таблицы состояний для ошибок
            AddTransition(IndicatorStates.Tracking, IndicatorEvents.CriticalError, IndicatorStates.Fault, setFault);
            AddTransition(IndicatorStates.Fault, IndicatorEvents.ResetCommand, IndicatorStates.SensorSearching, findSensor);
            AddTransition(IndicatorStates.Fault, IndicatorEvents.SensorSet, IndicatorStates.SensorSearching, findSensor);
            
       }

    }
    }



   
    